﻿using System;
using System.Data;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.IO;
using System.Text;


//use clr serialization to serialize the intermediate result
//optimizer property
//optimizer property
//optimizer property
//maximum size in bytes of persisted value
[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined, IsInvariantToNulls = true, IsInvariantToDuplicates = false, IsInvariantToOrder = true, MaxByteSize = -1)]
public class ModeAgg : IBinarySerialize
{
    private StringBuilder recs_buffer;


    // Initialize the internal data structures
    public void Init()
    {
        this.recs_buffer = new StringBuilder();
    }

    // Accumulate the next value, not if the value is null
    public void Accumulate(SqlString rec_value)
    {
        if (rec_value.IsNull)
        {
            return;
        }

        this.recs_buffer.Append(rec_value.Value).Append(',');
    }

    // Merge the partially computed aggregate with this aggregate.
    public void Merge(ModeAgg other)
    {
        this.recs_buffer.Append(other.recs_buffer);
    }

    // Called at the end of aggregation, to return the results of the aggregation.
    public SqlString Terminate()
    {
        string[] m_arr;
        string buff = string.Empty;
        string output = string.Empty;
        Int64 cnt = 0;
        Int64 h1_cnt = 0;
        Int64 h2_cnt = 0;
        
        //delete the trailing comma, if any
        if (this.recs_buffer != null && this.recs_buffer.Length > 0)
        {
            m_arr = this.recs_buffer.ToString(0, this.recs_buffer.Length - 1).Split(',');
            Array.Sort(m_arr);

            output = null;
            buff = null;

            // parse the array and count the occurences of each value
            for (Int64 i = 0; i < m_arr.Length; i++)
            {
                //check if value has changed
                // and either increase or reset counter
                if (m_arr[i] == buff)
                {
                    cnt++;
                } // end if (m_arr[i] != buffer)
                else
                {
                    cnt = 1;
                    buff = m_arr[i];
                }

                // stores 2 highest counts 
                if (cnt >= h1_cnt)
                {
                    h2_cnt = h1_cnt;
                    h1_cnt = cnt;
                    output = buff;
                }
            } // end for

            // if at least 2 values have same count return null
            if (h1_cnt == h2_cnt)
                output = null;
        
        } //end if (this.recs_buffer != null && this.recs_buffer.Length > 0)
        else
            output = null;

        return new SqlString(output);
    } // end Terminate() ---------------------------------------------------------------

    public void Read(BinaryReader r)
    {
        recs_buffer = new StringBuilder(r.ReadString());
    }

    public void Write(BinaryWriter w)
    {
        w.Write(this.recs_buffer.ToString());
    }
}
